home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 November: Tool Chest / Dev.CD Nov 98 TC.toast / Sample Code / Snippets / QuickDraw / Restore Screen Cluts / WindowPositioner.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-01-18  |  22.5 KB  |  542 lines  |  [TEXT/MPS ]

  1. /******************************************************************************\
  2. *
  3. * Apple Macintosh Developer Technical Support
  4. *
  5. * Main program file for the ColorReset application
  6. *
  7. * Program: ColorReset
  8. * File:    WindowPositioner.c
  9. *
  10. * by:      Forrest Tanaka
  11. *
  12. * Copyright © 1988-1992 Apple Computer, Inc.
  13. * All rights reserved.
  14. *
  15. \******************************************************************************/
  16.  
  17.  
  18. /******************************************************************************\
  19. * Header Files
  20. \******************************************************************************/
  21.  
  22. #ifndef THINK_C
  23. #include <Memory.h>
  24. #include <Script.h>
  25. #endif
  26.  
  27. #include <GestaltEqu.h>
  28. #include "WindowPositioner.h"
  29.  
  30.  
  31. /******************************************************************************\
  32. * Constants
  33. \******************************************************************************/
  34.  
  35. #define kAlertFactor 3 /* Denom of fraction of screen area above alert */
  36.  
  37. /* Collision rectangle specifications */
  38. #define kCollInitH  0  /* Initial offset of collision rect from base rect */
  39. #define kCollInitV  0  /* Initial offset of collision rect from base rect */
  40. #define kCollWidth  16 /* Width of collision rectangle in pixels */
  41. #define kCollHeight 16 /* Height of collision rectangle in pixels */
  42.  
  43. /* Staggered-window wrapping options */
  44. #define kNotWrapped  0 /* Collision testing hasn’t wrapped yet */
  45. #define kHorzWrapped 1 /* Collision testing wrapped horizontally */
  46. #define kVertWrapped 2 /* Collision testing wrapped vertically */
  47.  
  48. /* Coordinates of temporary off-screen window for calculating bias */
  49. #define kOffScreenTop    -32008 /* Top coordinate of off-screen window */
  50. #define kOffScreenLeft   -32008 /* Left coordinate of off-screen window */
  51. #define kOffScreenBottom -32000 /* Bottom coordinate of off-screen window */
  52. #define kOffScreenRight  -32000 /* Right coordinate of off-screen window */
  53.  
  54. #ifndef THINK_C
  55. #define topLeft(r) (*((Point *) &(r).top))
  56. #define botRight(r) (*((Point *) &(r).bottom))
  57. #define screenBits qd.screenBits
  58. #endif
  59.  
  60.  
  61. /******************************************************************************\
  62. * Prototypes
  63. \******************************************************************************/
  64.  
  65. void StaggerScreenRect(
  66.     Rect  *baseRect,
  67.     Rect  *modWindowRect,
  68.     short hBias,
  69.     short vBias);
  70.  
  71. GDHandle GetWindowGDevice(
  72.     WindowPtr theWindow);
  73.  
  74. void GetGlobalWindowRect(
  75.     WindowPtr theWindow,
  76.     Rect      *globalRect);
  77.  
  78.  
  79. /******************************************************************************\
  80. * Public: PositionScreenRect
  81. *
  82. * PositionScreenRect concerns itself with two rectangles: the base rectangle and
  83. * the window rectangle.  The window rectangle is simply the rectangle of the
  84. * window as passed in the windowRect parameter.  The base rectangle is what the
  85. * window rectangle should be positioned on.  If the screenOption specifies that
  86. * the window rectangle should be positioned based on a screen, then the base
  87. * rectangle is the global rectangle of the screen that the window should be
  88. * displayed on.  If the screenOption specifies that the window rectangle should
  89. * be positioned relative to the position of another window, then the base
  90. * rectangle is the global coordinates of the portRect of that window.
  91. *
  92. * Two screen options (specified by the screenOption parameter) specify that a
  93. * window should be based on one screen or another.  Multiple screens are only
  94. * supported on Color QuickDraw machines, so PositionScreenRect starts off by
  95. * eliminating the parentScreenPos option on black & white QuickDraw machines by
  96. * changing the kParentScreenPos option to the kMainScreenPos option.  Some
  97. * companies offer multiple screens for black & white QuickDraw machines, but
  98. * there’s no standard way to get at the rectangles of those other screens.
  99. *
  100. * The base rectangle is calculated first.  If the screen option is
  101. * kMainScreenPos, then the base rectangle can be copied from the screenBits.
  102. * bounds QuickDraw global.  If the screen option is kParentPos, then the base
  103. * rectangle is a copy of the portRect of the parent window (specified by the
  104. * parentWindow parameter) offset so that it has global coordinates.  If the
  105. * screen option is kParentScreenPos, then the base rectangle is the rectangle of
  106. * the screen that the parent window has most of its area on.  If the screen
  107. * option isn’t any of these, then the point (0,0) is returned and nothing more
  108. * is done.
  109. *
  110. * After the base rectangle is calculated, then the position of the window on
  111. * that base rectangle is calculated.  If the position option is kCenterPos, then
  112. * the window rectangle is centered right smack in the middle of the base
  113. * rectangle.  If the position option is kAlertPos, then the window rectangle is
  114. * centered horizontally, but it’s higher than the center vertically.  The amount
  115. * higher that it should be is specified by the kAlertFactor constant.  This
  116. * constant specifies the proportion of the vertical screen space aside from the
  117. * space taken up by the window should appear below the window.  For now,
  118. * kAlertFactor is 3, which means that there should be three times more screen
  119. * space below the window than above.
  120. \******************************************************************************/
  121.  
  122. void PositionScreenRect(
  123.     Rect      *windowRect,    /* Rectangle to center */
  124.     short     screenOption,   /* Options for screen to center against */
  125.     short     positionOption, /* Centering options */
  126.     WindowPtr parentWindow,   /* Pointer to parent window, if any */
  127.     short     hBias,          /* portRect.left-strucRgn.rgnBBox.left */
  128.     short     vBias)          /* portRect.top-strucRgn.rgnBBox.top */
  129. {
  130.     long     qdVersion;        /* Version of QuickDraw in use */
  131.     Rect     baseRect;         /* Rectangle to center against */
  132.     GDHandle maxGDevice;       /* GDevice containing most of parentWindow */
  133.     short    vCenterFactor;    /* ÷ left-over screen by this to get v coord */
  134.     Point    upperLeft;        /* Returns position of portRect.topLeft */
  135.     Boolean  goodScreenOption; /* True if screen option is valid */
  136.     Boolean  hasCQD;           /* True if Color QuickDraw is available */
  137.  
  138.     /* Determine whether Color QuickDraw is available or not */
  139.     (void)Gestalt( gestaltQuickdrawVersion, /*<*/&qdVersion );
  140.     hasCQD = qdVersion >= gestalt8BitQD;
  141.  
  142.     /* If want parent window’s screen but no CQD, then assume main screen */
  143.     if (screenOption == kParentScreenPos && !hasCQD)
  144.         screenOption = kMainScreenPos;
  145.  
  146.     /* If parent window is nil but it’s needed, then assume main screen */
  147.     if ((screenOption == kParentPos || screenOption == kParentScreenPos) &&
  148.             parentWindow == nil)
  149.         screenOption = kMainScreenPos;
  150.  
  151.     /* Assume the specified screen option is good */
  152.     goodScreenOption = true;
  153.  
  154.     /* Find the base rectangle */
  155.     if (screenOption == kMainScreenPos)
  156.     {
  157.         /* Base rectangle is the main screen */
  158.         baseRect = screenBits.bounds;
  159.         baseRect.top += GetMBarHeight();
  160.     }
  161.     else if (screenOption == kParentPos)
  162.         /* Base rectangle is the parent window’s portRect */
  163.         GetGlobalWindowRect( parentWindow, /*<*/&baseRect );
  164.     else if (screenOption == kParentScreenPos)
  165.     {
  166.         /* Base rectangle is screen containing most of parent window */
  167.         maxGDevice = GetWindowGDevice( parentWindow );
  168.         baseRect = (**maxGDevice).gdRect;
  169.  
  170.         /* If window’s GDevice is main screen’s, then take menu bar out */
  171.         if (maxGDevice == GetMainDevice())
  172.             baseRect.top += GetMBarHeight();
  173.     }
  174.     else
  175.     {
  176.         /* Invalid screen option */
  177.         goodScreenOption = false;
  178.     }
  179.  
  180.     /* Calculate a proper location for the window if screen option is valid */
  181.     if (goodScreenOption)
  182.     {
  183.         if (positionOption == kStaggerPos)
  184.         {
  185.             StaggerScreenRect( &baseRect, /*◊*/windowRect, hBias, vBias );
  186.             if (windowRect->right > baseRect.right - (kCollWidth / 2))
  187.                 windowRect->right = baseRect.right - (kCollWidth / 2);
  188.             if (windowRect->bottom > baseRect.bottom - (kCollHeight / 2))
  189.                 windowRect->bottom = baseRect.bottom - (kCollHeight / 2);
  190.         }
  191.         else if (positionOption == kCenterPos || positionOption == kAlertPos)
  192.         {
  193.             /* Find amount to divide vertical screen area left over */
  194.             if (positionOption == kCenterPos)
  195.                 vCenterFactor = 2;
  196.             else if (positionOption == kAlertPos)
  197.                 vCenterFactor = kAlertFactor;
  198.  
  199.             /* Calc left and top coordinates of destination rectangle */
  200.             upperLeft.h = (((baseRect.right - baseRect.left) - (windowRect->
  201.                     right - windowRect->left)) >> 1) + baseRect.left;
  202.             upperLeft.v = (((baseRect.bottom - baseRect.top) - (windowRect->
  203.                     bottom - windowRect->top)) / vCenterFactor) + baseRect.top;
  204.  
  205.             /* Offset the window rectangle to upperLeft */
  206.             OffsetRect( /*◊*/windowRect, upperLeft.h - windowRect->left,
  207.                     upperLeft.v - windowRect->top );
  208.         }
  209.     }
  210. }
  211.  
  212.  
  213. /******************************************************************************\
  214. * Private: StaggerScreenRect - Find staggered position for a window
  215. *
  216. * This routine returns a point that, if applied to the top-left corner of the
  217. * rectangle specified by windowRect, puts windowRect into staggered position
  218. * within the rectangle specified by baseRect.  baseRect, windowRect, and the
  219. * returned point are all assumed to be in global (screen) coordinates.
  220. *
  221. * “Staggered position” means that an attempt is made to position windowRect such
  222. * that its top-left corner isn’t close to the top-left corner of an existing
  223. * window’s frame.  To do this, a “collision rectangle” located near the top-left
  224. * corner of baseRect is created and placed into the collRect local variable.
  225. * The window list is searched to see if the top-left corner of any window frames
  226. * are in the collision rectangle, which would constitute a collision.
  227. * Initially, no collisions are tolerated.  If even one colliding window is
  228. * found, then the collision rectangle is moved down and to the right and the
  229. * window list is searched for collisions again.  If a collision is again found,
  230. * then the collision rectangle is moved down and to the right again and so
  231. * forth.
  232. *
  233. * A working copy of windowRect (StaggerScreenRect doesn’t modify windowRect)
  234. * in the local variable, workWindowRect, follows collRect such that it’s top-
  235. * left corner is at the center of collRect.  As collRect is offset after a
  236. * collision is found, workWindowRect is offset by the same amount.
  237. *
  238. * If any part of workWindowRect falls outside of baseRect, then collRect and
  239. * workWindowRect wrap around to the side of baseRect opposite to the side that
  240. * workWindowRect fell off of, and collision-testing resumes there.  This
  241. * wrapping is only allowed in one direction though.  For example, if
  242. * workWindowRect falls off of the right edge of baseRect, then it and collRect
  243. * wrap around to the left side of baseRect at the same vertical position.  If
  244. * the bottom edge of workWindowRect subsequently falls off of the bottom edge of
  245. * baseRect, then it won’t wrap around to the top because horizontally wrapping
  246. * has already happened.  Instead, collRect and workWindowRect are set back into
  247. * their initial positions at the top-left corner of baseRect.  If instead,
  248. * workWindowRect first falls off of the bottom edge of baseRect, then it and
  249. * collRect wrap around to the top of baseRect at the same horizontal position.
  250. * If the right edge of workWindowRect subsequently falls off of the right edge
  251. * of baseRect, then it won’t wrap around to the left because vertical wrapping
  252. * has already happened.  Instead, collRect and workWindowRect are again set back
  253. * into their initial positions at the top-left corner of baseRect.
  254. *
  255. * Of course, setting collRect and workWindowRect back to their initial positions
  256. * guarantees a collision.  So, StaggerScreenRect becomes more tolerant of
  257. * collisions.  After finding a collision at every attempt, it now allows one
  258. * collision before moving on to the next position.  If every position had more
  259. * than one collision, then StaggerScreenRect becomes even more tolerant and
  260. * allows up to two collisions.  This process continues until a suitable position
  261. * for workWindowRect is found.
  262. *
  263. * In all truth, workWindowRect isn’t an EXACT copy of windowRect.  The value of
  264. * the hBias parameter is added to both horizontal coordinates and the value of
  265. * vBias is added to both vertical coordinates.  hBias should hold the number of
  266. * pixels between the top coordinate of the portRect of the window being
  267. * positioned and the top coordinate of the window’s frame.  Similarly, vBias
  268. * should hold the number of pixels between the left coorindate of the portRect
  269. * and the left cooordinate of the window’s frame.
  270. \******************************************************************************/
  271.  
  272. static void StaggerScreenRect(
  273.     Rect  *baseRect,      /* Rectangle within which to stagger window rect */
  274.     Rect  *modWindowRect, /* Port rectangle to be staggered within baseRect */
  275.     short hBias,          /* portRect.left-strucRgn.rgnBBox.left */
  276.     short vBias)          /* portRect.top-strucRgn.rgnBBox.top */
  277. {
  278.     WindowPtr testWindow;       /* Window being tested to see if it collides */
  279.     Rect      testWindowRect;   /* testWindow’s window frame rectangle */
  280.     Rect      collRect;         /* Rectangle in which to test for collisions */
  281.     Rect      initCollRect;     /* Initial collRect at upper left of baseRect */
  282.     Rect      workWindowRect;   /* Working copy of modWindowRect */
  283.     short     windowRectWidth;  /* Width of modWindowRect */
  284.     short     windowRectHeight; /* Height of modWindowRect */
  285.     short     collCount;        /* # collisions found so far within collRect */
  286.     short     maxCollCount;     /* Maximum allowed collisions within collRect */
  287.     short     wrapStatus;       /* Tells how collRect has wrapped, if at all */
  288.     Boolean   foundSlot;        /* True if found good slot for modWindowRect */
  289.  
  290.     /* Set up the initial collision rectangle at offset from baseRect */
  291.     initCollRect.top = baseRect->top + kCollInitV;
  292.     initCollRect.left = baseRect->left + kCollInitH;
  293.     initCollRect.bottom = initCollRect.top + kCollHeight;
  294.     initCollRect.right = initCollRect.left + kCollWidth;
  295.  
  296.     /* Set up the working destination rectangle */
  297.     windowRectWidth = modWindowRect->right - modWindowRect->left;
  298.     windowRectHeight = modWindowRect->bottom - modWindowRect->top;
  299.     workWindowRect.top = initCollRect.top + vBias + kCollHeight / 2;
  300.     workWindowRect.left = initCollRect.left + hBias + kCollWidth / 2;
  301.     workWindowRect.bottom = workWindowRect.top + windowRectHeight;
  302.     workWindowRect.right = workWindowRect.left + windowRectWidth;
  303.  
  304.     /* Set up initial conditions for the search */
  305.     collRect = initCollRect;
  306.     maxCollCount = 0;
  307.     wrapStatus = kNotWrapped;
  308.     foundSlot = false;
  309.  
  310.     /* Search for a slot until an appropriate one is found */
  311.     while (!foundSlot)
  312.     {
  313.         /* See if a slot has <= maximum number of allowed collisions */
  314.         collCount = 0;
  315.         testWindow = FrontWindow();
  316.         while (testWindow != nil && collCount <= maxCollCount)
  317.         {
  318.             /* Get the global rectangle covering entire window frame */
  319.             testWindowRect = (**((WindowPeek)testWindow)->strucRgn).rgnBBox;
  320.  
  321.             /* If top left of window frame in testWindowRect, then collision */
  322.             if (PtInRect( topLeft( testWindowRect ), &collRect ))
  323.                 collCount++;
  324.  
  325.             /* Go to the next window */
  326.             testWindow = (WindowPtr)((WindowPeek) testWindow)->nextWindow;
  327.         }
  328.  
  329.         /* If too many collisions, then shift collision rect to the next slot */
  330.         if (collCount > maxCollCount)
  331.         {
  332.             /* Shift collision rectangle to the next slot */
  333.             OffsetRect( /*◊*/&collRect, kCollWidth / 2, kCollHeight / 2 );
  334.             OffsetRect( /*◊*/&workWindowRect, kCollWidth / 2, kCollHeight / 2 );
  335.             if (workWindowRect.bottom > baseRect->bottom)
  336.             {
  337.                 if (wrapStatus != kHorzWrapped)
  338.                 {
  339.                     /* Wrap collision rect vertically */
  340.                     collRect.top = initCollRect.top;
  341.                     collRect.bottom = initCollRect.bottom;
  342.                     wrapStatus = kVertWrapped;
  343.                 }
  344.                 else
  345.                 {
  346.                     /* Wrapped horz, try from start & allow 1 more collision */
  347.                     collRect = initCollRect;
  348.                     maxCollCount++;
  349.                 }
  350.  
  351.                 /* Make workWindowRect follow collRect */
  352.                 workWindowRect.top = collRect.top + vBias + kCollHeight / 2;
  353.                 workWindowRect.left = collRect.left + hBias + kCollWidth / 2;
  354.                 workWindowRect.bottom = workWindowRect.top + windowRectHeight;
  355.                 workWindowRect.right = workWindowRect.left + windowRectWidth;
  356.             }
  357.             if (workWindowRect.right > baseRect->right)
  358.             {
  359.                 if (wrapStatus != kVertWrapped)
  360.                 {
  361.                     /* Wrap collision rect horizontally */
  362.                     collRect.left = initCollRect.left;
  363.                     collRect.right = initCollRect.right;
  364.                     wrapStatus = kHorzWrapped;
  365.                 }
  366.                 else
  367.                 {
  368.                     /* Wrapped vert, try from start & allow 1 more collision */
  369.                     collRect = initCollRect;
  370.                     maxCollCount++;
  371.                 }
  372.  
  373.                 /* Make workWindowRect follow collRect */
  374.                 workWindowRect.top = collRect.top + vBias + kCollHeight / 2;
  375.                 workWindowRect.left = collRect.left + hBias + kCollWidth / 2;
  376.                 workWindowRect.bottom = workWindowRect.top + windowRectHeight;
  377.                 workWindowRect.right = workWindowRect.left + windowRectWidth;
  378.             }
  379.         }
  380.         else
  381.             foundSlot = true;
  382.     }
  383.  
  384.     /* Return the top-left corner of workWindowRect */
  385.     OffsetRect( /*◊*/modWindowRect, workWindowRect.left - modWindowRect->left,
  386.             workWindowRect.top - modWindowRect->top );
  387. }
  388.  
  389.  
  390. /******************************************************************************\
  391. * Private: GetWindowGDevice - Get GDevice that contains most of a window
  392. *
  393. * This routine searches through all active screen GDevices in the GDevice list
  394. * for the GDevice of the screen that has the greatest area of intersection with
  395. * the portRect of the window specified by theWindow.  A handle to this GDevice
  396. * is returned.  If the portRect of theWindow is doesn’t intersect any active
  397. * screen, then nil is returned.
  398. *
  399. * This routine can only be called if Color QuickDraw is implemented because
  400. * the Graphics Device Manager only exists on Color QuickDraw machines.
  401. \******************************************************************************/
  402.  
  403. static GDHandle GetWindowGDevice(
  404.     WindowPtr theWindow) /* Pointer to window being tested */
  405. {
  406.     GDHandle testGDevice; /* Handle to GDevice being tested */
  407.     GDHandle maxGDevice;  /* GDevice with maximum intersection area */
  408.     Rect     windowRect;  /* Rect of window’s portRect in global coords */
  409.     Rect     commonRect;  /* Rect of intersection between windowRect & gdRect */
  410.     long     maxArea;     /* Max area of GDevice/portRect intersection found */
  411.     long     commonArea;  /* Area of intersection between windowRect & gdRect */
  412.  
  413.     /* gdRects in global coords, so get window’s portRect in global coords */
  414.     GetGlobalWindowRect( theWindow, /*<*/&windowRect );
  415.  
  416.     /* Loop through all active screen GDevices */
  417.     testGDevice = GetDeviceList();
  418.     maxArea = 0;
  419.     maxGDevice = nil;
  420.     while (testGDevice != nil)
  421.         {
  422.         /* Only test if GDevice is active screen GDevice */
  423.         if (TestDeviceAttribute( testGDevice, screenDevice ) &&
  424.                 TestDeviceAttribute ( testGDevice, screenActive ))
  425.             /* Only check area if window and gdRect intersect */
  426.             if (SectRect( &(**testGDevice).gdRect, &windowRect,
  427.                     /*<*/&commonRect ))
  428.             {
  429.                 /* Find area common to GDevice.gdRect and window’s portRect */
  430.                 commonArea = (long)(commonRect.right - commonRect.left) *
  431.                         (commonRect.bottom - commonRect.top);
  432.  
  433.                 /* If area > max area found, then update maxArea & maxGDevice */
  434.                 if (commonArea > maxArea)
  435.                 {
  436.                     maxArea = commonArea;
  437.                     maxGDevice = testGDevice;
  438.                 }
  439.             }
  440.  
  441.         /* Try the next GDevice */
  442.         testGDevice = GetNextDevice( testGDevice );
  443.     }
  444.  
  445.     return maxGDevice;
  446. }
  447.  
  448.  
  449. /******************************************************************************\
  450. * Private: GetGlobalWindowRect - Get a window’s portRect in global coordinates
  451. *
  452. * The portRect of the window specified by theWindow is converted from the
  453. * window’s local coordinates to global (screen) coordinates.  This converted
  454. * rectangle is returned in globalRect.  If theWindow is nil, then globalRect
  455. * returns the rectangle [T:0 L:0 B:0 R:0].
  456. \******************************************************************************/
  457.  
  458. static void GetGlobalWindowRect(
  459.     WindowPtr theWindow,   /* Pointer to window whose global rect we want */
  460.     Rect      *globalRect) /* Returns theWindow’s portRect in global coords */
  461. {
  462.     GrafPtr savedPort; /* Pointer to current GrafPort; for restoring */
  463.  
  464.     if (theWindow != nil)
  465.     {
  466.         GetPort( /*<*/&savedPort );
  467.         SetPort( theWindow );
  468.         *globalRect = theWindow->portRect;
  469.         LocalToGlobal( /*◊*/&topLeft( *globalRect ) );
  470.         LocalToGlobal( /*◊*/&botRight( *globalRect ) );
  471.         SetPort( savedPort );
  472.     }
  473.     else
  474.         SetRect( /*<*/globalRect, 0, 0, 0, 0 );
  475. }
  476.  
  477.  
  478. #pragma segment Window Position
  479. /******************************************************************************\
  480. * Public: CalcWindowBias
  481. *
  482. * The only reliable way to calculate the bias of a window is actually to create
  483. * a visible window and measure the resulting thickness of the window frame, and
  484. * then to dispose of the window immediately after the measurement is taken.  To
  485. * avoid visual threats against good taste, the window is created far outside of
  486. * any possible screen position, and it’s created behind any other windows to
  487. * avoid deactivating existing windows.
  488. *
  489. * To avoid as much heap disruption as possible, the window record is pre-
  490. * allocated as a handle, which is then locked.  Then, NewWindow is called to
  491. * create the ephemeral window.  The bias is then calculated by subtracting the
  492. * left and top coordinates of the structure region from the left and top
  493. * coordinates of the content region.
  494. \******************************************************************************/
  495.  
  496. Point CalcWindowBias(
  497.     short   procID,     /* Defproc ID of window whose bias is being calculated */
  498.     Boolean goAwayFlag) /* True if window has a go-away box */
  499. {
  500.     Point      bias;        /* Calculated bias */
  501.     WindowPeek biasWindow;  /* Pointer to temp window used for bias calc */
  502.     Handle     windowStore; /* Handle to window record storage (kept locked) */
  503.     Rect       windowRect;  /* Rectangle of window in global coords */
  504.     GrafPtr    savedPort;   /* Pointer to current GrafPort for restoring */
  505.  
  506.     /* In case an error happens, default to zero bias */
  507.     bias.h = bias.v = 0;
  508.  
  509.     /* Allocate WindowRecord as relocatable to avoid cluttering heap */
  510.     windowStore = NewHandle( sizeof (WindowRecord) );
  511.     if (windowStore != nil)
  512.     {
  513.         GetPort( /*<*/&savedPort );
  514.  
  515.         /* WindowRecord must be locked */
  516.         HLock( windowStore );
  517.  
  518.         /* Calc rectangle that’s almost certain to be off any screen */
  519.         SetRect( /*<*/&windowRect, kOffScreenLeft, kOffScreenTop,
  520.                 kOffScreenRight, kOffScreenBottom );
  521.  
  522.         /* Create visible temporary window "behind" all existing windows */
  523.         biasWindow = (WindowPeek)NewWindow( *windowStore, &windowRect, "\P",
  524.                 true, procID, nil, goAwayFlag, 0L );
  525.         if (biasWindow != nil)
  526.         {
  527.             /* Bias is content region top-left - structure region top-left */
  528.             bias.h = (**biasWindow->contRgn).rgnBBox.left - (**biasWindow->
  529.                     strucRgn).rgnBBox.left;
  530.             bias.v = (**biasWindow->contRgn).rgnBBox.top - (**biasWindow->
  531.                     strucRgn).rgnBBox.top;
  532.         }
  533.  
  534.         /* Pretend this never happened */
  535.         SetPort( savedPort );
  536.         CloseWindow( (WindowPtr)biasWindow );
  537.         DisposHandle( windowStore );
  538.     }
  539.  
  540.     return bias;
  541. }
  542.